﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using Eneta.Portal.Common.Definitions;
using Eneta.Portal.Common.Dto;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Publishing;
using System.Web;

namespace Eneta.Portal.Common.Repository
{
    /// <summary>
    /// Common repository for news items.
    /// </summary>
    public class NewsItemRepository : BaseRepository 
    {
        private readonly string _newsUrl;

        public NewsItemRepository(string newsUrl) : base(null)
        {
            _newsUrl = newsUrl;
        }

        protected AnnouncementDto CreateAnnouncement(SPListItem item)
        {
            if(item==null)
            {
                Logger.LogInfo("item is null");
                return null;
            }

            var dto = new AnnouncementDto
            {
                Id = item.ID,
                Title = item.Title,
                Created = (DateTime)item["Created"],
                Modified = (DateTime)item["Modified"],
                FileName = item["FileLeafRef"].ToString()
            };

            var pubWeb = PublishingWeb.GetPublishingWeb(item.Web);
            var url = EnetaApplication.PortalAnnouncementsSiteUrl;
            if (!url.EndsWith("/"))
                url += "/";
            url += pubWeb.PagesListName;
            url += "/" + item["FileLeafRef"];

            dto.DisplayUrl = url;

            if (item[AnnouncementFields.Body] != null)
                dto.Body = item[AnnouncementFields.Body].ToString().Replace("&quot;", "\"");
            
            if (item.ModerationInformation != null)
                dto.Status = item.ModerationInformation.Status;
            else
            {
                Logger.LogWarning("CreateAnnouncement(): item has no moderation status");
                dto.Status = SPModerationStatusType.Denied;
            }

            SPFieldUserValue userValue;
            if (item[AnnouncementFields.RealAuthor] != null)
                userValue = new SPFieldUserValue(item.Web, item[AnnouncementFields.RealAuthor].ToString());
            else
                userValue = new SPFieldUserValue(item.Web, item["Author"].ToString());

            if (userValue != null)
            {
                var user = userValue.User;

                if (user != null)
                {
                    dto.OwnerUserName = user.Name;
                    dto.InfoSourceName = user.Name;

                    var profileRepository = new UserProfileRepository(item.Web);
                    var profile = profileRepository.GetUserProfileByUserName(user.LoginName);

                    if (profile != null)
                    {
                        dto.OwnerProfileId = profile.Id;
                        dto.InfoSourceUrl = EnetaApplication.PortalProfilePageUrl + "?profileID=" + profile.Id;
                        dto.OwnerProfileName = profile.Title;
                    }
                }
            }

            // TODO: Why is keywords field not found by guid?
            int fldidx = 0;
            foreach (SPField field in item.Fields)
                if (field.Id.ToString().ToUpper() == AnnouncementFields.Keywords.ToString().ToUpper())
                {
                    break;
                }
                else
                {
                    fldidx++;
                }
            

            if (item[fldidx] != null)
            {
                var keywordValues = new SPFieldLookupValueCollection(item[fldidx].ToString());
                if (keywordValues.Count > 0)
                    foreach (var keyword in keywordValues)
                    {
                        var keywordDto = new KeywordDto
                                             {
                                                 Id = keyword.LookupId,
                                                 Title = keyword.LookupValue,
                                             };
                        dto.Keywords.Add(keywordDto);
                    }
            }

            if (item.Web.HasList("Comments"))
            {
                var commentsList = item.Web.Lists["Comments"];

                var queryString = "<Where><Eq><FieldRef Name='NewsItem' LookupId='True'/><Value Type='Lookup'>{0}</Value></Eq></Where>";
                queryString = string.Format(queryString, item.ID);
                var commentItems = ExecuteQuery(commentsList, queryString);
                FillComments(dto, commentItems);
            }
            else
            {
                Logger.LogWarning("Cannot find comments list for announcements");
            }
            

            if (item[AnnouncementFields.CompanyId] != null)
            {
                var companiesValues = new SPFieldLookupValueCollection(item[AnnouncementFields.CompanyId].ToString());
                var companyRepository = new CompanyRepository(EnetaApplication.PortalCompaniesSiteUrl);

                foreach(var companyValue in companiesValues)
                {
                    var company = companyRepository.GetCompanyById(companyValue.LookupId);
                    if(company != null)
                        dto.Companies.Add(company);
                }
            }

            return dto;
        }

        /// <summary>
        /// Gets the announcement by id.
        /// </summary>
        /// <param name="id">The announcement id.</param>
        public AnnouncementDto GetAnnouncementById(int id)
        {
            AnnouncementDto announcement = null;

            var sec = new SPSecurity.CodeToRunElevated(delegate
            {
                using (var site = new SPSite(EnetaApplication.PortalAnnouncementsSiteUrl))
                using (var web = site.OpenWeb(GetWebUrl(site, EnetaApplication.PortalAnnouncementsSiteUrl)))
                {
                    announcement = GetAnnouncementById(web, id);
                }
            });
            SPSecurity.RunWithElevatedPrivileges(sec);
            return announcement;
        }

        private AnnouncementDto GetAnnouncementById(SPWeb web, int id)
        {
            var listName = PublishingWeb.GetPagesListName(web);
            var list = web.Lists[listName];
            var item = list.GetItemById(id);
            var blog = CreateAnnouncement(item);
            return blog;
        }

        /// <summary>
        /// Fills given news item DTO comments list with the comments from comments collection.
        /// </summary>
        /// <param name="dto">The news item DTO.</param>
        /// <param name="commentItems">The comments list.</param>
        public void FillComments(NewsItemDto dto, SPListItemCollection commentItems)
        {
            foreach (SPListItem commentItem in commentItems)
            {
                dto.Comments.Add(CreateComment(dto, commentItem));
            }
        }

        /// <summary>
        /// Gets the common news items RSS as string.
        /// </summary>
        public string GetNewsItemsRss()
        {
            var blogRep = new BlogRepository(EnetaApplication.PortalBlogSiteUrl);
            var annRep = new AnnouncementRepository(EnetaApplication.PortalAnnouncementsSiteUrl);

            var blogItems = blogRep.GetApprovedEntries(1, 25).Results.Cast<NewsItemDto>();
            var annItems = annRep.GetApprovedAnnouncements(1, 25).Results.Cast<NewsItemDto>();

            var news = new List<NewsItemDto>();
            news.AddRange(blogItems);
            news.AddRange(annItems);

            news = news.OrderByDescending(n => n.Created).Take(25).ToList();

            var title = "Eneta portaal: Uudised";
            var url = "http://www.eneta.ee/uudised/";

            return CreateRss(title, url, news);
        }

        /// <summary>
        /// Gets the comments RSS as string.
        /// </summary>
        public string GetCommentsRss()
        {
            var blogRep = new BlogRepository(EnetaApplication.PortalBlogSiteUrl);
            var annRep = new AnnouncementRepository(EnetaApplication.PortalAnnouncementsSiteUrl);

            var blogItems = blogRep.GetBlogComments(25);
            var annItems = annRep.GetComments(25);

            var comments = new List<CommentDto>();
            comments.AddRange(blogItems);
            comments.AddRange(annItems);

            comments = comments.Take(25).ToList();

            var title = "Eneta portaal: Uudised";
            var url = "http://www.eneta.ee/uudised/";
            return CreateCommentsRss(title, url, comments);
        }

        private string CreateRss(string title, string viewUrl, IEnumerable<NewsItemDto> entries)
        {
            var builder = new StringBuilder(GetRssHeader(title, viewUrl));
            if (entries == null)
            {
                builder.Append(GetRssFooter());
                return builder.ToString();
            }

            foreach (var entry in entries)
            {
                if (entry == null)
                    continue;

                string site, entryUrl;
                if (entry.GetType() == typeof(BlogEntryDto))
                {
                    site = "blogid";
                    //entryUrl = "BlogPost";
                }
                else
                {
                    site = "teated";
                    //entryUrl = "AnnouncementView";
                }
                builder.Append("<item>");
                builder.Append("<title>");
                if (!string.IsNullOrEmpty(entry.Title))
                    builder.Append(entry.Title.Replace("&", "&amp;"));
                builder.Append("</title>\r\n");

                builder.Append("<link>");
                builder.Append("http://www.eneta.ee/uudised/" + site + "/Lehed/" + entry.FileName);
                builder.Append("</link>\r\n");

                builder.Append("<comments>");
                builder.Append("http://www.eneta.ee/uudised/" + site + "/Lehed/" + entry.FileName);
                builder.Append("#comments</comments>\r\n");

                builder.Append("<pubDate>");
                builder.Append(entry.Created.ToString("r"));
                builder.Append("</pubDate>\r\n");

                builder.Append("<dc:creator>");
                builder.Append(entry.OwnerUserName);
                builder.Append("</dc:creator>\r\n");

                builder.Append("<guid isPermaLink=\"false\">");
                builder.Append("http://www.eneta.ee/uudised/" + site + "/Lehed/" + entry.FileName);
                builder.Append("</guid>\r\n");

                builder.Append("<content:encoded><![CDATA[");
                //builder.Append(RegExHelper.DetectAndReplaceUrls(post.Body));
                if (!string.IsNullOrEmpty(entry.Body))
                    if (entry.Body.Length > 255)
                        builder.Append(entry.Body.Substring(0, 255) + "...");
                    else
                        builder.Append(entry.Body);
                builder.Append("]]></content:encoded>\r\n");                

                builder.Append("<slash:comments>");
                builder.Append(entry.Comments.Count);
                builder.Append("</slash:comments>");

                builder.Append("</item>");
            }

            builder.Append(GetRssFooter());
            return builder.ToString();
        }


        protected string CreateCommentsRss(string title, string viewUrl, IEnumerable<CommentDto> comments)
        {
            var builder = new StringBuilder(GetRssHeader(title, viewUrl));
            if (comments == null)
            {
                builder.Append(GetRssFooter());
                return builder.ToString();
            }

            foreach (var comment in comments)
            {
                if (comment == null)
                    continue;

                string site, entryUrl;
                if (comment.Parent == null)
                    continue;
                if (comment.Parent.GetType() == typeof(BlogEntryDto))
                {
                    site = "blogid";
                    //entryUrl = "BlogPost";
                }
                else
                {
                    site = "teated";
                    //entryUrl = "AnnouncementView";
                }
                builder.Append("<item>");
                builder.Append("<title>Re: ");
                if (!string.IsNullOrEmpty(comment.Parent.Title))
                    builder.Append(comment.Parent.Title.Replace("&", "&amp;"));
                builder.Append("</title>\r\n");

                builder.Append("<link>");
                builder.Append("http://www.eneta.ee/uudised/" + site + "/Lehed/" + comment.Parent.FileName + "#" + comment.Id);
                builder.Append("</link>\r\n");

                builder.Append("<pubDate>");
                builder.Append(comment.Created.ToString("r"));
                builder.Append("</pubDate>\r\n");

                builder.Append("<dc:creator>");
                builder.Append(comment.OwnerName);
                builder.Append("</dc:creator>\r\n");

                builder.Append("<guid isPermaLink=\"false\">");
                builder.Append("http://www.eneta.ee/uudised/" + site + "/Lehed/" + comment.Parent.FileName + "#" + comment.Id);
                builder.Append("</guid>\r\n");

                builder.Append("<content:encoded><![CDATA[");
                builder.Append(comment.Body);
                builder.Append("]]></content:encoded>\r\n");                
                builder.Append("</item>");
            }

            builder.Append(GetRssFooter());
            return builder.ToString();
        }


        protected CommentDto CreateComment(NewsItemDto parent, SPListItem item)
        {
            var dto = new CommentDto
                          {
                              Id = item.ID,
                              Title = item.Title,
                              Created = (DateTime)item["Created"],
                              Modified = (DateTime)item["Modified"],
                              Parent = parent
                          };

            if(item["Comment"] != null)
                dto.Body = item["Comment"].ToString();

            if(item["Author"] != null)
            {
                var authorValue = new SPFieldUserValue(item.Web, item["Author"].ToString());
                var author = authorValue.User;

                if(author != null)
                {                    
                    dto.OwnerUserName = author.Name;
                    var repository = new UserProfileRepository(item.Web);
                    var profile = repository.GetUserProfileByUserName(author.LoginName);

                    if (profile != null)
                    {
                        dto.OwnerName = profile.UserName;
                        dto.OwnerProfileId = profile.Id;
                    }
                }
            }

            return dto;
        }

        public PagedResult<NewsItemDto> GetApprovedNews(int page, int pageSize)
        {
            var result = new PagedResult<NewsItemDto>();
            var sec = new SPSecurity.CodeToRunElevated(delegate 
                {
                    using(var site = new SPSite(_newsUrl))
                    using (var web = site.OpenWeb(GetWebUrl(site, EnetaApplication.PortalNewsSiteUrl)))
                    using (var webBlogs = site.OpenWeb(GetWebUrl(site, EnetaApplication.PortalBlogSiteUrl)))
                    using (var webAnn = site.OpenWeb(GetWebUrl(site, EnetaApplication.PortalAnnouncementsSiteUrl)))
                    {
                        var blogRepository = new BlogRepository(EnetaApplication.PortalBlogSiteUrl);

                        var blogEntryCtName = ContentTypes.GetName(ContentTypes.BlogEntry);
                        var announcementCtName = ContentTypes.GetName(ContentTypes.Announcement);

                        var query = new SPSiteDataQuery();
                        query.Query = "<Where><And><Or>";
                        query.Query += "<Eq><FieldRef Name='ContentType' /><Value Type='string'>{0}</Value></Eq>";
                        query.Query += "<Eq><FieldRef Name='ContentType' /><Value Type='string'>{1}</Value></Eq>";
                        query.Query += "</Or>";
                        query.Query += "<Eq><FieldRef Name='_ModerationStatus' /><Value Type='ModStat'>{2}</Value></Eq>";
                        query.Query += "</And></Where>";
                        //query.Query += "<OrderBy>";
                        //query.Query += "<FieldRef Name='PublishDate' Ascending='False' />";
                        //query.Query += "<FieldRef Name='Created' Ascending='False' />";
                        //query.Query += "</OrderBy>";

                        query.Query = string.Format(query.Query,blogEntryCtName, announcementCtName, Resources.ModerationStatusApproved);

                        query.Lists = "<Lists BaseType='1' />";
                        query.Webs = "<Webs Scope='Recursive' />";
                        //query.ViewFields = "<FieldRef Name='ContentType' /><FieldRef Name='_ModerationStatus' />";
                        query.ViewFields = "<FieldRef Name='ContentType' />";

                        var items = web.GetSiteData(query);

                        var resultRows = new List<DataRow>();

                        result.Page = (page <= 0 ? 1 : page);
                        result.PageSize = (pageSize <= 0 ? items.Rows.Count : pageSize);
                        result.PageCount = (int)Math.Ceiling(items.Rows.Count / (decimal)result.PageSize);  
                        var startRow = Math.Min((result.Page - 1)*result.PageSize, items.Rows.Count);

                        for (var i = startRow; i < Math.Min(startRow + result.PageSize, items.Rows.Count); i++)
                        {
                            resultRows.Add(items.Rows[i]);
                        }

                        var announcementIdList = new List<int>(10);
                        var blogEntryIdList = new List<int>(10);

                        foreach (DataRow dr in resultRows)
                        {
                            if (dr["ContentType"] != null)
                            {
                                var id = int.Parse(dr["ID"].ToString());
                                if (dr["ContentType"].ToString() == ContentTypes.GetName(ContentTypes.Announcement))
                                    announcementIdList.Add(id);
                                else if (dr["ContentType"].ToString() == ContentTypes.GetName(ContentTypes.BlogEntry))
                                    blogEntryIdList.Add(id);
                            }
                        }

                        if (announcementIdList.Count > 0)
                        {
                            var annQueryString = string.Empty;
                            for (var i = 0; i < announcementIdList.Count - 2; i++)
                            {
                                annQueryString += "<Or>";
                            }

                            for (var i = 0; i < announcementIdList.Count; i++)
                            {
                                annQueryString += "<Eq><FieldRef Name='ID' /><Value Type='int'>" + announcementIdList[i] +
                                                  "</Value></Eq>";
                                if (i == 0)
                                    continue;
                                annQueryString += "</Or>";
                            }
                            announcementIdList.Clear();
                            annQueryString = "<Where>" + annQueryString + "</Where>";

                            var annQuery = new SPQuery
                                               {Query = annQueryString, RowLimit = (uint) announcementIdList.Count};
                            var annListName = PublishingWeb.GetPagesListName(webAnn);
                            var annItems = webAnn.Lists[annListName].GetItems(annQuery);
                            foreach (SPListItem annItem in annItems)
                                result.Results.Add(CreateAnnouncement(annItem));
                        }

                        if (blogEntryIdList.Count > 0)
                        {
                            var blogQueryString = string.Empty;
                            for (var i = 0; i < blogEntryIdList.Count - 2; i++)
                            {
                                blogQueryString += "<Or>";
                            }

                            for (var i = 0; i < blogEntryIdList.Count; i++)
                            {
                                blogQueryString += "<Eq><FieldRef Name='ID' /><Value Type='int'>" + blogEntryIdList[i] +
                                                   "</Value></Eq>";
                                if (i == 0)
                                    continue;
                                blogQueryString += "</Or>";
                            }
                            blogEntryIdList.Clear();
                            blogQueryString = "<Where>" + blogQueryString + "</Where>";

                            var blogQuery = new SPQuery { Query = blogQueryString, RowLimit = (uint)blogEntryIdList.Count };
                            var entryListName = PublishingWeb.GetPagesListName(webBlogs);
                            var entryItems = webBlogs.Lists[entryListName].GetItems(blogQuery);
                            foreach (SPListItem entryItem in entryItems)
                            {
                                var entry = blogRepository.CreateBlogEntry(entryItem, true);
                                result.Results.Add(entry);
                            }

                            var orderedResult = from r in result.Results
                                                orderby r.PublishDate descending
                                                select r;
                            result.Results = orderedResult.ToList();
                        }
                    }
                });
            SPSecurity.RunWithElevatedPrivileges(sec);
            return result;
        }

        /// <summary>
        /// Gets the list of announcements for given user.
        /// </summary>
        /// <param name="userId">The user id.</param>
        /// <param name="count">The number of announcements to return.</param>
        public IList<AnnouncementDto> GetUserAnnouncements(int userId, int count)
        {
            IList<AnnouncementDto> companies = new List<AnnouncementDto>();

            var sec = new SPSecurity.CodeToRunElevated(delegate
            {
                using (var site = new SPSite(EnetaApplication.PortalAnnouncementsSiteUrl))
                using (var web = site.OpenWeb(GetWebUrl(site, EnetaApplication.PortalAnnouncementsSiteUrl)))
                {
                    companies = GetUserAnnouncements(web, userId, count);
                }
            });
            SPSecurity.RunWithElevatedPrivileges(sec);

            return companies;
        }

        private IList<AnnouncementDto> GetUserAnnouncements(SPWeb web, int userId, int count)
        {
            var annCtName = ContentTypes.GetName(ContentTypes.Announcement);
            var queryString = "<Where><And><Eq><FieldRef Name='RealAuthor' LookupId='True' /><Value Type='User'>{0}</Value></Eq>";
            queryString += "<Eq><FieldRef Name='ContentType' /><Value Type='Text'>{1}</Value></Eq></And></Where>";
            queryString += "<OrderBy><FieldRef Name='Created' Ascending='False'/></OrderBy>";
            queryString = string.Format(queryString, userId, annCtName);

            var listName = PublishingWeb.GetPagesListName(web);
            var list = web.Lists[listName];
            var items = ExecuteQuery(list, queryString, count);
            var result = CreateAnnouncements(items);

            return result;
        }

        private IList<AnnouncementDto> CreateAnnouncements(SPListItemCollection items)
        {
            IList<AnnouncementDto> result = new List<AnnouncementDto>();

            if (items == null)
                return result;

            foreach (SPListItem item in items)
                result.Add(CreateAnnouncement(item));

            return result;
        }
    }
}
